﻿using System;
using System.Collections.Generic;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Moq;
using ScrumFactory.Composition.ViewModel;
using ScrumFactory.Composition;
using ScrumFactory.ProjectTeam;
using ScrumFactory.ProjectTeam.ViewModel;
using ScrumFactory.Services;
using ScrumFactory;

namespace ScumFactory.ProjectTeam.Tests {

    /// <summary>
    /// Test the Project Team View Model.
    /// </summary>
    [TestClass()]
    public class ProjectTeamViewModelTest {

        [TestMethod()]
        public void ShowRolesListCommand_ShouldShowRolesListView() {

            // (1) SETUP MOCKS
            var aggregator = SetupAggregator();            
            var view = new Mock<IRolesListView>();

            // (2) CREATE TARGET
            var target = new ProjectTeamViewModel(aggregator.Object, null, null, null);
            target.RolesListView = view.Object;
            target.ShowRolesListCommand.Execute(null);

            // (3) ASSERT TESTS
            aggregator.Verify(
                a => a.Publish<IPanelViewModel>(ScrumFactoryEvent.OpenNewWindow, It.IsAny<IPanelViewModel>()),
                "ShowRolesListCommand did not publish OpenNewWindow event");
        }

        [TestMethod()]
        public void ProjectTeam_ShouldLoadRoles() {

            // (1) SETUP MOCKS
            var aggregator = SetupAggregator();
            var executor = SetupExecutor();
            var projectsService = SetupService();
            var teamService = new Mock<ITeamService>();
                        
            // (2) CREATE TARGET
            project1.Roles = null;
            var target = new ProjectTeamViewModel(aggregator.Object, projectsService.Object, teamService.Object, executor.Object);
            
            // (3) ASSERT TESTS
            aggregator.Verify(
                a => a.Publish<ICollection<Role>>(ScrumFactoryEvent.ProjectRolesLoaded, It.IsAny<ICollection<Role>>()),
                "ProjectTeam did not publish ProjectRolesLoaded event");
            Assert.IsTrue(project1.Roles.Contains(role1), "Role1 was not assigned to the project");
            Assert.IsTrue(project1.Roles.Contains(role2), "Role2 was not assigned to the project");
            

            projectsService.Verify(
                s => s.GetProjectRoles(project1.ProjectUId),
                "ProjectTeam did not load roles from the services");
        }


        #region Setup Mocks

        Project project1 = new Project { ProjectUId = "project1" };
        Role role1 = new Role() { RoleUId = "Role 1" };
        Role role2 = new Role() { RoleUId = "Role 2" };

        Mock<IEventAggregator> SetupAggregator() {
            var aggregator = new Mock<IEventAggregator>();
            aggregator.Setup(a => a.Subscribe<Project>(ScrumFactoryEvent.ViewProjectDetails, It.IsAny<Action<Project>>())).Callback<ScrumFactoryEvent, Action<Project>>((e, del) => {
                del(project1);
            });
            return aggregator;
        }

        Mock<IProjectsService> SetupService() {
            var service = new Mock<IProjectsService>();
            List<Role> roles = new List<Role>() { role1, role2 };
            service.Setup(s => s.GetProjectRoles("project1")).Returns(roles);
            return service;
        }

        Mock<IBackgroundExecutor> SetupExecutor() {
            var executor = new Mock<IBackgroundExecutor>();


            executor.Setup(e => e.StartBackgroundTask<ICollection<Role>>(
                It.IsAny<Func<ICollection<Role>>>(),
                It.IsAny<Action<ICollection<Role>>>()))
                .Callback<Func<ICollection<Role>>, Action<ICollection<Role>>>((a, c) => {
                    var r = a();
                    c(r);
                });

            executor.Setup(e => e.StartBackgroundTask(
                It.IsAny<Action>(),
                It.IsAny<Action>()))
                .Callback<Action, Action>((a, c) => {
                    a();
                    c();
                });
            return executor;
        }


        #endregion
    }
}
